//
//  SelectCarViewModel.swift
//  Automobile
//
//  Created by erkebulan elzhan on 8/19/20.
//  Copyright © 2020 erkebulan elzhan. All rights reserved.


import UIKit

protocol SelectCarViewModelDelegate {
    func getCarInfo (car: Car)
    func createPost (id: Int )
}

final class SelectCarViewModel {
    
    var delegate: ViewModelProtocol!
    
    var viewModelDelegate: SelectCarViewModelDelegate!
    
    var myCars: [CarModel] = []
    
    var currentCar: Car?
    
    var createdAd: Post = Post()
    
    var saveToGarage: Bool = true
    
    let backendService = Backend()
}

// MARK: - Configuring Cars

extension SelectCarViewModel {

    func removeCar (car: Car) {
        
        var request = URLRequest(url: Request(base: .garage).getUrl(path: "garage/cars/\(car.id!)/"))
        request.httpMethod = "DELETE"

        delegate.updateSpinner(showSpinner: true)
        
        backendService.fetchFromBackend(for: Bool.self, request: request, statusCode: 200, sendBool: true) { (result) in
            
            self.delegate.updateSpinner(showSpinner: false)
            
            switch result {
            case .failure(let error):
                self.delegate.didHappenError(error: error)
            case .success(_):
                if let index = self.myCars.firstIndex(where: {
                    $0.car.id == car.id
                }) {
                    self.myCars.remove(at: index)
                }
                self.delegate.updatePosts()
            }
        }
    }
    
    func loadMyCars () {
        let request = URLRequest(url: Request(base: .garage).getUrl(path: "garage/cars/"))
        delegate.updateSpinner(showSpinner: true)
        
        backendService.fetchFromBackend(for: Response<[Car]>.self, request: request, statusCode: 200) { (result) in
            self.delegate.updateSpinner(showSpinner: false)
            
            switch result {
            case .failure(let err):
                self.delegate.didHappenError(error: err)
            case .success(let response):
                var output: [CarModel] = []
                for res in response.results {
                    output.append(CarModel(car: res))
                }
                self.myCars = output
                self.delegate.updatePosts()
            }
        }
    }
    
    func getCarData (id: Int) {
        
        let request = URLRequest(url: Request(base: saveToGarage ? .garage : .advert).getUrl(path: saveToGarage ? "garage/cars/\(id)/" : "cars/\(id)/"))
        
        delegate.updateSpinner(showSpinner: true)
        
        backendService.fetchFromBackend(for: Car.self, request: request, statusCode: 200) { (result) in
            
            self.delegate.updateSpinner(showSpinner: false)
            
            switch result {
                case .failure(let error):
                    self.delegate.didHappenError(error: error)
                case .success(let car):
                    self.currentCar = car
                    self.viewModelDelegate.getCarInfo(car: car)
            }
        }
    }
        
    func addNewCar (car: Car) {
        let requestURL = Request(base: saveToGarage ? .garage : .advert).getUrl(path: saveToGarage ? "garage/cars/" : "cars/create/")
        var request = URLRequest(url: requestURL)
        request.httpMethod = "POST"
        delegate.updateSpinner(showSpinner: true)
        
        backendService.fetchFromBackend(for: Car.self, request: request, statusCode: 201, sendBody: true, body: self.getAddCarParameters(car: car)) { (result) in
            
            self.delegate.updateSpinner(showSpinner: false)
            switch result {
                case .failure(let error):
                    self.delegate.didHappenError(error: error)
                case .success(let newCar):
                    self.getCarData(id: newCar.id!)
            }
        }
    }
    
    func sendAd (ad: Post) {
        
        struct PostId: Decodable {
            var id: Int
            var car:Int
        }
        
        let urlString = Request(base: .advert).getUrl(path: "adverts/my/")
        var request = URLRequest(url: urlString)
        request.httpMethod = "POST"
        
        self.delegate.updateSpinner(showSpinner: true)
        
        backendService.fetchFromBackend(for: PostId.self, request: request, statusCode: 201, sendBody: true, body: self.getSendAdParameters(ad: ad)) { (result) in
            switch result {
            case .failure(let error):
                self.delegate.didHappenError(error: error)
                self.delegate.updateSpinner(showSpinner: false)
            case .success(let postId):
                self.createdAd.id = postId.id
                self.sendImages(images: ad.localImgs ?? [], advert: postId.id, carID: postId.car, options: ad.customOptions)
            }
        }
    }
}


// MARK: - Utils

extension SelectCarViewModel {
    
    private func getAddCarParameters (car: Car) -> [String: Any?] {
        var parameters = [ "car_mark": car.marka?.id, "car_model": car.model?.id, "car_generation": car.generation?.id, "car_serie": car.serie?.id, "car_modification": car.modification?.id, "car_color": car.color?.id, "car_type": car.carType?.id, "mileage": car.mileage ] as [String : Any?]
        
        if saveToGarage {
            parameters["state_number"] = car.carNumber?.fullNumber
            parameters["title"] = car.carName
        }
        
        return parameters
    }
    
    private func getSendAdParameters (ad: Post) -> [String:Any] {
        var keys = ["cleared_by_customs", "exchange", "city", "contact_name", "contact_email", "price", "contact_phones"]
        var values = [ad.isRastomozhen, ad.isObmen, ad.city?.id, ad.contactName, ad.contactEmail, ad.price, self.setUpNumbers(numbers: ad.contactNumbers ?? [])] as [Any?]
        
        if ad.saveToGarage {
            keys.append("car_ext")
            values.append(ad.car.id)
        } else {
            keys.append("car")
            values.append(ad.car.id)
        }
        
        var data: [String:Any] = [:]
        
        for (i, key) in keys.enumerated() {
            if let item = values[i] {
                data[key] = item
            }
        }
        
        return data
    }
    
    private func createFileBody(parameters: [String: String]?, filePathKey: String?, imageDataKey: Data, boundary: String, filename: String) -> Data {
        let body = NSMutableData();

        if parameters != nil {
            for (key, value) in parameters! {
                body.appendString("--\(boundary)\r\n")
                body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
                body.appendString("\(value)\r\n")
            }
        }

        let mimetype = "image/jpg"

        body.appendString("--\(boundary)\r\n")
        body.appendString("Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(filename)\"\r\n")
        body.appendString("Content-Type: \(mimetype)\r\n\r\n")
        body.append(imageDataKey)
        body.appendString("\r\n")
        body.appendString("--\(boundary)--\r\n")

        return body as Data
    }
    
    private func generateBoundaryString() -> String {
        return "Boundary-\(NSUUID().uuidString)"
    }

    private func setUpNumbers (numbers: [NumberViewModel.Number]) -> [Int] {
        var output: [Int] = []
        for number in numbers {
            output.append(number.id)
        }
        return output
    }
}


// MARK: - Sending Ad

extension SelectCarViewModel {

    private func sendOptions (options: [String:Any], carID: Int) {
        var request = URLRequest(url: Request(base: .advert).getUrl(path: "cars/custom-options/"))
        request.httpMethod = "POST"
        
        var customOptions = options
        customOptions["car"] = carID
        
        backendService.fetchFromBackend(for: Bool.self, request: request, statusCode: 201, sendBool: true, sendBody: true, body: customOptions) { (result) in
            self.delegate.updateSpinner(showSpinner: false)
            switch result {
            case .failure(let err):
                self.delegate.didHappenError(error: err)
                self.viewModelDelegate.createPost(id: carID)
            case .success(_):
                self.viewModelDelegate.createPost(id: carID)
            }
        }
    }
    
    private func sendImages (images: [UIImage], advert: Int, carID: Int, options: [String:Any]?) {
        var results: [Result<Bool, RequestError>] = []
        
        if images.count == 0 {
            self.handleOptions(carID: carID, options: options)
        }
        
        for (i, image) in images.enumerated() {
            self.sendImage(advert: advert, image: image, index: i) { (result) in
                results.append(result)
                if (results.count == images.count) {
                    self.handleOptions(carID: carID, options: options)
                }
            }
        }
    }
    
    private func handleOptions (carID: Int, options: [String: Any]? = nil) {
        if let customOptions = options {
            self.sendOptions(options: customOptions, carID: carID)
        } else {
            self.delegate.updateSpinner(showSpinner: false)
            self.viewModelDelegate.createPost(id: carID)
        }
    }
        
    private func sendImage (advert: Int, image: UIImage, index: Int, completion: @escaping (Result<Bool, RequestError>) -> Void ){
        
        var request = URLRequest(url: Request(base: .advert).getUrl(path: ("adverts/my/images/")))
        request.httpMethod = "POST"
        
        let boundary = generateBoundaryString()
        
        request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
        request.setValue(User.shared.token, forHTTPHeaderField: "Authorization-Type")
        
        let param = ["advert": "\(advert)"]
        
        if let imageData = image.jpegData(compressionQuality: 1) {
            request.httpBody = createFileBody(parameters: param, filePathKey: "image", imageDataKey: imageData, boundary: boundary, filename: "post\(index).jpg")
            
            backendService.fetchFromBackend(for: Bool.self, request: request, statusCode: 201, sendBool: true, defaultHeaders: false) { (result) in
                switch result {
                case .failure(let error):
                    completion(.failure(error))
                case .success(_):
                    completion(.success(true))
                }
            }
        } else {
            completion(.failure(.parameter("Ошибка при конвертации изображения")))
        }
    }
}
